class MeuParser extends Parser;
options {
      buildAST=true;
}
{
    String str;
    Hashtable symboltable = new Hashtable();
    int identacao = 2;
    int inLoop = 0;

    Symbol.Type auxtype;

    /* Responsavel por identar os itens. */
    private void printIdentacao() {
        for (int i = 0; i < identacao; i++) {
            System.out.print("\t");
        }
    }

    /* Imprime o conteudo da linha (um atalho para System.out.print()) */
    private void print(String str) {
        System.out.print(str);
    }

    /*
     * Imprime o conteudo da linha com quebra de linha (um atalho para
     * System.out.println())
     */
    private void println(String str) {
        System.out.println(str);
    }

    /*
     * Incrementa identacao.
     */
    private void incrIdentacao() {
        identacao++;
    }

    /*
     * Decrementa identacao.
     */
    private void decrIdentacao() {
        identacao--;
    }

    /*
     * Incrementa contador de loops.
     */
    private void incrInLoop() {
        inLoop++;
    }

    /*
     * Decrementa contador de loops.
     */
    private void decrInLoop() {
        inLoop--;
    }

    private void checkInLoop() {
        if (inLoop <= 0) {
            println("\nErro: nao e' possivel usar \"pare\" ou \"continue\" fora de um laco");
            System.exit(1);
        }
    }

    private void addSymbol(String symbolname, Symbol.Type type, int scope)
    {
        Symbol s = new Symbol();
        s.setType(type);
        s.setScope(scope);
        symboltable.put(symbolname, s);
    }

    private void checkSymbol(String symbolname) {
        if (!symboltable.containsKey(symbolname)) {
            println("\nErro: identificador >" + symbolname + "< nao declarado.");
            System.exit(1);
        }
    }

    private Symbol getSymbol(String symbolname) {
        checkSymbol(symbolname);
        return (Symbol)symboltable.get(symbolname);
    }
}

prog: (EOL)*
      {
        println("import java.util.*;");
        println("");
        println("public class Main {");
        println("");
        println("\tpublic static void main(String[] args) {\n");
        println("\t\tScanner scanner = new Scanner(System.in);\n\n");
      }
      "programa"
      (EOL)+
      (declare)*
      corpo
      "fimprog" T_ponto
      {
        println("\t}");
        println("}");
      };

declare: {printIdentacao();} "declare" tipo lista_id
         T_ponto { println(";"); } (EOL)+ ;

tipo: "real"     {auxtype = Symbol.Type.Double; print("double ");}
    | "inteiro"   {auxtype = Symbol.Type.Integer; print("int ");}
    | "string"   {auxtype = Symbol.Type.String; print("String ");};

lista_id: T_id
          {
            print(LT(0).getText());
            addSymbol(LT(0).getText(), auxtype, identacao);
          }
          (T_virgula T_id
            {
                /* Insere o simbolo na tabela de simbolos */
                addSymbol(LT(0).getText(), auxtype, identacao);
                print(", " + LT(0).getText());
            }
          )* ;

corpo: (cmd)+;

cmd: {printIdentacao();}
     (cmd_leitura | cmd_escrita | cmd_expr
   |  cmd_if | cmd_while | cmd_do | cmd_pare | cmd_continue) (EOL)+ ;

cmd_leitura: "leia"
            L_Paren T_id {
                switch (getSymbol(LT(0).getText()).getType()) {
                    case Integer:
                        print(LT(0).getText() + " = scanner.nextInt()");
                        break;
                    case String:
                        print(LT(0).getText() + " = scanner.nextLine()");
                        break;
                    case Double:
                        print(LT(0).getText() + " = scanner.nextDouble()");
                        break;
                }
            }
            R_Paren
            T_ponto { println(";"); };

cmd_escrita: "escreva"
             {print("System.out.println");}
             L_Paren
             ( T_texto
             | T_id {checkSymbol(LT(0).getText());}) {print("(" + LT(0).getText() + ")");}
             R_Paren
             T_ponto { println(";"); };

cmd_expr: T_id {checkSymbol(LT(0).getText()); print(LT(0).getText());}
          T_attr expr T_ponto { println(";"); };

cmd_if: "se" {print("if");}
        expr_logica "entao" (EOL)* bloco 
            (cmd_else)?;

cmd_else: {printIdentacao();} "senao" {print("else");} (EOL)* bloco;

cmd_while: expr_while (EOL)* {incrInLoop();} bloco {decrInLoop();} ;

cmd_do: "faca" {print("do");} (EOL)* bloco (EOL)* {printIdentacao();}
        expr_while T_ponto { println(";"); };

cmd_pare: "pare" T_ponto { checkInLoop(); println("break;");};

cmd_continue: "continue" T_ponto { checkInLoop(); println("continue;");};

expr_while: "enquanto" {print("while");} expr_logica;

bloco:  L_Chave {println(" {"); incrIdentacao();} (EOL)*
(cmd)* {decrIdentacao(); printIdentacao();} R_Chave {println("}");} ;

expr_logica: L_Paren {print(" (");} expr T_oprel expr R_Paren {print(")");} ;

expr : termo (T_mais termo | T_menos termo)*;

termo : fator (T_vezes fator | T_div fator | T_mod fator)*;

fator : T_num {print(LT(0).getText());}
      | T_id {checkSymbol(LT(0).getText()); print(LT(0).getText());}
      | L_Paren {print("(");} expr R_Paren {print(")");}  ;


class MeuLexico extends Lexer;

options
{
  testLiterals = false;
  caseSensitive = true;
  charVocabulary = '\0'..'\377';
  k = 2;
}

L_Paren: '(' ;
R_Paren: ')' ;
L_Chave: '{' ;
R_Chave: '}' ;

EOL: ('\n'|"\r\n") { newline(); };
T_space: ('\t'|' ') { _ttype = Token.SKIP; };

T_mais: '+' {System.out.print(" + "); } ;
T_menos: '-' {System.out.print(" - "); };
T_vezes: '*' {System.out.print(" * "); };
T_div: '/' {System.out.print(" / "); };
T_mod: '%' {System.out.print(" % "); };

T_oprel: '<'  {System.out.print(" < ");}
       | '>'  {System.out.print(" > ");}
       | "<=" {System.out.print(" <= ");}
       | ">=" {System.out.print(" >= ");}
       | "!=" {System.out.print(" != ");}
       | "==" {System.out.print(" == ");};
T_attr: ":="  {System.out.print(" = ");};
T_virgula: ',';
T_ponto: ';' ;

T_texto: '"' ('a'..'z'|'A'..'Z'|'0'..'9'|'?'|','|T_space)* '"';
T_num: ('0'..'9')+ ('.' ('0'..'9')+)? ;

T_id options {testLiterals=true;}
     : ('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9')*;

/* vim: set ft=antlr et sts=4: */
